
/*
 * 
 * @author Nebojsa Trninic
 * @date   07.09.2012.
 *
 * Implementation of Invoker of Background Commands, i.e. IBC.
 *
 */


#include "ibc.h"

using namespace IBC;
using namespace std;


unsigned char CInvokerBackgroundCommands::g_nRC = 0;


void CBackgroundThread::run( void *arg )
{
    cout << "CBackgroundThread::run() begin" << endl;

    if( m_pIBC == NULL) {
        cerr << "CBackgroundThread::run() Invoker should not be NULL.";
        assert( 0 );
        return; // Finish Bg thread.
    } // if sanity check

    m_pIBC->RCinc(); // Increase ref. count since beside main invoker thread,
                     // also Bg thread is referencing Invoker after main calls thread start().

    {
        cout << "CBackgroundThread::run() cmd mutex lock" << endl;
        CMutexLock lmtxCmdCond( m_pIBC->m_mtxCmdCond ); // Bg thread locks Cond mutex, to sync with main during startup.
    } // Unlock early mutex, so main thread can timeout in timedwait() and obtain mutex and run in case of timeout.

    ICommand *pCmd = m_pIBC->m_pCmd; // Command stored in Invoker.
    if( pCmd == NULL) {
        cerr << "CBackgroundThread::run() Invoker command should not be NULL.";
        assert( 0 );
        m_pIBC->RCdec();
        return; // Finish Bg thread.
    } // if sanity check

    CCondition *pCond = m_pIBC->m_pCndBgThr;

    if( pCond == NULL) {
        cerr << "CBackgroundThread::run() Invoker condition should not be NULL.";
        assert( 0 );
        m_pIBC->RCdec();
        return; // Finish Bg thread.
    } // if sanity check    

    cout << "CBackgroundThread::run() command execute begin" << endl;
    pCmd->execute(); // Can take arbitrary long time. After this Bg Thread should check if Invoker is
                     // still waiting for results.

    cout << "CBackgroundThread::run() command execute end" << endl;

    CInvokerBackgroundCommands::TInvokerState nInvokerState = m_pIBC->getInvokerState();
    unsigned char nRC = m_pIBC->RCget(); // needed to assert later.
    bool bSignalFinish = false;

    switch( nInvokerState )
    {
        case CInvokerBackgroundCommands::IS_S_INEXEC:
            // Invoker still waiting for command to finish in Bg Thread.
            cout << "CBackgroundThread::run() After command, Invoker state INEXEC." << endl;
            assert( nRC == 2);
            // Signal main thread that command is done, but only after RCdec later.
            bSignalFinish = true;
            break;
        case CInvokerBackgroundCommands::IS_E_TIMEOUT_LINGER:
            // Invoker is not waiting for results anymore, so RC==1 only for Bg thread.
            cout << "CBackgroundThread::run() After command, Invoker state LINGER." << endl;
            assert( nRC == 1);
            break;
        default:
            // Should not happen.
            cerr << "CBackgroundThread::run() After command, Invoker state unexpected " << nInvokerState << endl;
            assert( 0 );
            break;
    }; // switch

    cout << "CBackgroundThread::run() ref count dec" << endl;
    m_pIBC->RCdec(); // Bg Thread is not going to use invoker anymore, so it releases its reference.

    if( bSignalFinish )
    {
        assert( m_pIBC->RCget() == 1); // Must be 1, since Bg thr decremented its reference and main is waiting.
        pCond->signal(); // Really signal only in the end after RCdec() 2->1.
    }

    cout << "CBackgroundThread::run() end" << endl;

} // CBackgroundThread::run()



CInvokerBackgroundCommands::TInvokerState CInvokerBackgroundCommands::getInvokerState( void )
{
    cout << "getInvokerState() begin" << endl;
    // Retrieve Invoker state in thread safe manner.
    CMutexLock l( m_mtxInvSta );
    cout << "getInvokerState() end state == " << m_nInvokerState << endl;

    return m_nInvokerState;
}

void CInvokerBackgroundCommands::setInvokerState( TInvokerState v_nSta )
{
    cout << "setInvokerState() begin" << endl;
    // Set Invoker state in thread safe manner. Private.
    CMutexLock l( m_mtxInvSta );

    m_nInvokerState = v_nSta;
    cout << "setInvokerState() end state == " << m_nInvokerState << endl;
}

void CInvokerBackgroundCommands::RCinc( void )
{
    cout << "RCinc() begin" << endl;
    CMutexLock l( m_mtxRC );

    m_nRC++;
    g_nRC++;

    cout << " RC == " << (int) m_nRC << endl;
    cout << "gRC == " << (int) g_nRC << endl;
    cout << "RCinc() end" << endl;
}

void CInvokerBackgroundCommands::RCdec( void )
{
    cout << "RCdec() begin" << endl;

    bool bShouldDestruct = false; // Needed because of locking/unlocking mutex that is going to be deleted
                                  // in case ref count reaches 0, when it shoul self destruct.

    { // locking scope
        CMutexLock l( m_mtxRC );

        if( m_nRC == 0 )
        {
            cerr << "Sanity check failed. Caller should never do this." << endl;
            assert( 0 );
            return;
        }

        m_nRC--;
        g_nRC--;

        cout << " RC == " << (int) m_nRC << endl;
        cout << "gRC == " << (int) g_nRC << endl;
    
        if( m_nRC == 0 )
        {
            cout << "RCdec() ref count 0, so Invoker is going to delete itself and cause destructor to be called." << endl;
            // No more references 1->0, so Invoker should be deleted and thus all resources deallocated.
            //delete this; // Free Invoker itself. In destructor, all attributes are deallocated. Cannot do it here, since
                           // it would destroy mutex m_mtxRC that needs to be unlocked.
            bShouldDestruct = true; // postpone destruct for later, so that m_mtxRC lives to be unlocked.
        } // if no more refs
    } // locking scope - causes unlock of m_mtxRC, so it can be deleted below.

    if( bShouldDestruct )
        delete this; // Free Invoker itself. In destructor, all attributes are deallocated.

    cout << "RCdec() end" << endl;

} // void CInvokerBackgroundCommands::RCdec( void )


unsigned char CInvokerBackgroundCommands::RCget( void )
{
    cout << "RCget() begin" << endl;
    CMutexLock l( m_mtxRC );
    cout << " RC == " << (int) m_nRC << endl;
    cout << "gRC == " << (int) g_nRC << endl;
    cout << "RCget() end" << endl;

    return m_nRC;
}


CInvokerBackgroundCommands::~CInvokerBackgroundCommands()
{
    cout << "~CInvokerBackgroundCommands() begin" << endl;
    assert( m_nRC == 0 );

    // m_pThrBg, i.e. background thread takes care of itself, once its run() finishes,
    // so it should not be deleted here, only assigned NULL ptr.
    m_pThrBg = NULL;

    CMutexLock lc( m_mtxCmdCond ); // Accessing command, so lock.
    cout << "~CInvokerBackgroundCommands() after lock" << endl;

    if( m_pCmd != NULL )
    {
        cout << "~CInvokerBackgroundCommands() deleting command" << endl;
        delete m_pCmd;
        m_pCmd = NULL;
    }
    else
        cout << "~CInvokerBackgroundCommands() command already NULL, so not deleted." << endl;

    if( m_pCndBgThr != NULL )
    {
        cout << "~CInvokerBackgroundCommands() deleting condition" << endl;
        delete m_pCndBgThr;
        m_pCndBgThr = NULL;
    }
    else
        cout << "~CInvokerBackgroundCommands() condition already NULL, so not deleted." << endl;

    cout << "~CInvokerBackgroundCommands() end" << endl;

} // destructor CInvokerBackgroundCommands::~CInvokerBackgroundCommands()


// Caller should call RCdec() after processing Invoker state after the call, in order to signal that
// he is not referencing Invoker and so that ref counting can properly clean up when neither main
// nor Bg thread are referencing Invoker anymore.
void CInvokerBackgroundCommands::execCmdInBgThr( void )
{
    cout << "execCmdInBgThr() begin" << endl;
    // Sanity check
    assert( getInvokerState() == IS_S_PENDING );
    assert( RCget() == 1 ); // Only main Invoker thead is referencing at the moment. Bg thread not started, yet.
    assert( m_pCndBgThr == NULL );
    assert( m_pThrBg == NULL );

    if( m_pCmd == NULL)
    {
        cerr << "execCmdInBgThr() command should not be NULL.";
        setInvokerState( IS_E_RESOURCES );
        assert( 0 );
        return;
    } // if sanity check

    try
    {
        cout << "execCmdInBgThr() allocate condition." << endl;
        m_pCndBgThr = new CCondition( &m_mtxCmdCond ); // Allocate condition for Bg Thread to signal its finish.
    }
    catch( exception &e )
    {
        cerr << "execCmdInBgThr() condition allocation problem" << e.what() << endl;
        setInvokerState( IS_E_RESOURCES );
        return;
    } // catch

    try
    {
        cout << "execCmdInBgThr() allocate Bg thread" << endl;
        m_pThrBg = new CBackgroundThread( this ); // Allocate Bg Thread.
    }
    catch( exception &e )
    {
        cerr << "execCmdInBgThr() Bg thread allocation problem." << e.what() << endl;
        setInvokerState( IS_E_RESOURCES );
        // Ref. counting is going to clean up condition in destructor.
        return;
    } // catch

    cout << "execCmdInBgThr() before lock" << endl;
    CMutexLock lmtxCmdCond( m_mtxCmdCond ); // Bg thread and invoker share Cmd and Cond mutex, so lock.
    cout << "execCmdInBgThr() after lock" << endl;
    
    // Start Cmd in Bg Thread, which will actually first block at mutex lock, after incrementing RC.
    m_pThrBg->start();
    cout << "execCmdInBgThr() after start" << endl;

    cout << "RC == " << (int) RCget() << endl;
    // assert( RCget() == 2 ); Depending on thread switching RC can be 1 or 2.

    setInvokerState( IS_S_INEXEC ); // Since there is Bg Thread that is going to exec command, we are in exec state.
    
    // Wait until command is executed or timeout happens, whichever happens sooner.
    unsigned long nS, nN; // To track time for timed wait.
    CThread::get_time( &nS, &nN, m_nTO, 0 ); // m_nTO seconds from now.

    cout << "execCmdInBgThr() before timedwait()" << endl;

    if ( ! m_pCndBgThr->timedwait( nS, nN )) // This unlocks command mutex, so Bg Thread can lock it and execute command.
    {   // Timeout, meaning that the command did not execute in timeout time, so we consider this an error.
        // Since current threading API does not offer kill method, we linger in both cases no matter the thread timeout policy.
        cout << "execCmdInBgThr() timeout, Bg Thread left to linger" << endl;
        setInvokerState( IS_E_TIMEOUT_LINGER );
        assert( RCget() == 2 ); // Invoker thread and Bg thread still both referencing. Main will soon RCdec() and disregard
                                // command results since they are still not ready and command is being executed in Bg thread.
    } // if timeout occured.
    else
    {   // Main thread is here only after Command executed fully, packed its result/error/exception and Bg thread
        // signalled this, performed a RCdec() and unlocked the mutex, which was then locked in this main invoker
        // thread which is ready to inform the caller through Invoker state. RC should be 1. Invoker after processing command
        // result/error/exception can RCdec() thus freeing Invoker and related command. Bg thread exits itself.
        cout << "execCmdInBgThr() S_DONE" << endl;
        setInvokerState( IS_S_DONE );
        assert( RCget() == 1 );
    } // not timeout, i.e. in time.

    cout << "execCmdInBgThr() end" << endl;

} // CInvokerBackgroundCommands::execCmdInBgThr()
